package com.example.lotterysystem.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JwtUtil;
import com.example.lotterysystem.common.utils.RegexUtil;
import com.example.lotterysystem.controller.param.UserLoginParam;
import com.example.lotterysystem.controller.param.UserPasswordLoginParam;
import com.example.lotterysystem.controller.param.UserRegisterParam;
import com.example.lotterysystem.dao.mapper.UserMapper;
import com.example.lotterysystem.dao.dataobject.Encrypt;
import com.example.lotterysystem.dao.dataobject.UserDO;
import com.example.lotterysystem.service.UserService;
import com.example.lotterysystem.service.dto.UserDTO;
import com.example.lotterysystem.service.dto.UserLoginDTO;
import com.example.lotterysystem.service.dto.UserRegisterDTO;
import com.example.lotterysystem.service.enums.UserIdentityEnum;
import jakarta.validation.constraints.NotBlank;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {
    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
    @Autowired
    private UserMapper userMapper;
    /**
     * 注册用户
     *
     * @param param
     * @return
     */
    @Override
    public UserRegisterDTO register(UserRegisterParam param) {
        //校验注册信息
        checkRegisterInfo(param);
        //加密私密数据(构造 dao 层请求)
        UserDO userDO = new UserDO();
        userDO.setUserName(param.getName());

        userDO.setEmail(param.getMail());
        userDO.setPhoneNumber(new Encrypt(param.getPhoneNumber())); //使用AES 方式加密手机号
        userDO.setIdentity(param.getIdentity());
        if(StringUtils.hasText(param.getPassword())){
            //如果密码不为空，说明是管理员，使用 sha256 加密
            userDO.setPassword(DigestUtil.sha256Hex(param.getPassword()));
        }
        //保存数据
        userMapper.insert(userDO);
        //构造返回

        //mock 数据
        UserRegisterDTO userRegisterDTO = new UserRegisterDTO();
        userRegisterDTO.setUserId(userDO.getId());
        return userRegisterDTO;
    }


    /**
     * 校验注册信息
     *
     * @param param
     */
    public void checkRegisterInfo(UserRegisterParam param) {
        // 判空
        if (null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);
        }

        // 校验邮箱格式
        if (!RegexUtil.checkMail(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);
        }

        // 校验手机号格式
        if (!RegexUtil.checkMobile(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }

        // 校验身份信息
        if (null == UserIdentityEnum.forName(param.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        // 校验管理员密码是否填写（必填）
        if (param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name()) && !StringUtils.hasText(param.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_EMPTY);
        }

        // 密码校验，至少 6 位
        if (StringUtils.hasText(param.getPassword()) && !RegexUtil.checkPassword(param.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        // 校验邮件是否被使用
        if (checkMailUsed(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }

        // 校验手机号是否被使用
        if (checkPhoneNumberUsed(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }

    }

    /**
     * 校验手机号是否被使用
     *
     * @param phoneNumber
     * @return
     */
    private boolean checkPhoneNumberUsed(@NotBlank(message = "电话不能为空！") String phoneNumber) {
        int count = userMapper.countByPhone(new Encrypt(phoneNumber));
        return count > 0;
    }

    /**
     * 校验邮箱是否被使用
     *
     * @param mail
     * @return
     */
    private boolean checkMailUsed(@NotBlank(message = "邮箱不能为空！") String mail) {
        // 需要去数据库中检查该邮箱是否已被使用
        int count = userMapper.countByMail(mail);
        return count > 0;
    }

    /**
     * 登录
     *
     * @param param
     * @return
     */
    @Override
    public UserLoginDTO login(UserLoginParam param) {
        UserLoginDTO  userLoginDTO ;
        // 类型检查与类型转换
        // Java 14 版本及以上可用以下一行代码完成上述过程
        if(param instanceof UserPasswordLoginParam loginParam){
            // 密码登录流程
            userLoginDTO = loginByUserPassword(loginParam);
        }else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_INFO_NOT_EXIST);
        }
        return userLoginDTO;
    }


    /**
     * 根据用户身份查找用户信息
     *
     * @param identity 用户身份枚举，用于筛选用户列表如果为null，则不进行筛选
     * @return 返回一个UserDTO列表，包含符合身份条件的用户信息
     */
    @Override
    public List<UserDTO> findUserInfo(UserIdentityEnum identity) {
        // 将枚举转换为字符串，以便在数据库查询中使用
        String identityString = null == identity ? null : identity.name();
        // 查表
        List<UserDO> userDOList = userMapper.findUserInfo(identityString);
        // 将查询结果转换为DTO列表
        List<UserDTO> userDTOList = userDOList.stream()
                .map(userDO -> {
                    UserDTO userDTO = new UserDTO();
                    userDTO.setUserId(userDO.getId());
                    userDTO.setUserName(userDO.getUserName());
                    userDTO.setEmail(userDO.getEmail());
                    userDTO.setPhoneNumber(userDO.getPhoneNumber().getValue());
                    userDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
                    return userDTO;
                }).collect(Collectors.toList());
        return userDTOList;
    }

    /**
     * 密码登录
     * @param loginParam
     * @return
     */
    private UserLoginDTO loginByUserPassword(UserPasswordLoginParam loginParam) {
        // 初始化用户对象
        UserDO userDO = null;

        // 检查登录名是否为有效的邮箱格式
        if (RegexUtil.checkMail(loginParam.getLoginName())) {
            // 邮箱登录
            // 根据邮箱查询用户表
            userDO = userMapper.selectByMail(loginParam.getLoginName());
            // 检查登录名是否为有效的手机号格式
        }else if (RegexUtil.checkMobile(loginParam.getLoginName())) {
            // 手机号登录
            // 根据手机号查询用户表
            userDO = userMapper.selectByPhoneNumber(new Encrypt(loginParam.getLoginName()));
        } else {
            // 如果登录名既不是有效的邮箱也不是有效的手机号，抛出服务异常，提示登录不存在
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_NOT_EXIST);

        }



        // 校验登录信息
        if (null == userDO) {
            // 如果用户信息为空，则抛出异常
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_EMPTY);
        } else if (StringUtils.hasText(loginParam.getMandatoryIdentity())
                // 如果登录参数中的身份信息与用户信息中的身份信息不匹配，则抛出异常
                && !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        } else if (!DigestUtil.sha256Hex(loginParam.getPassword()).equals(userDO.getPassword())) {
            // 如果密码经过SHA-256加密后的结果与数据库中的密码不匹配，则抛出异常
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        // 创建一个映射，用于存储用户登录后的信息
        Map<String, Object> claim = new HashMap<>();
        claim.put("userId", userDO.getId());
        claim.put("Identity", userDO.getIdentity());

        // 生成JWT令牌
        String token = JwtUtil.genJwtToken(claim);

        // 创建用户登录信息对象，并设置相关属性
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));

        // 返回用户登录信息对象
        return userLoginDTO;
    }



}
